/////////////////////////////////////////////////////////////////////////
// This file has been derived from an example by Andrew Pociu of       //
// http://www.geekpedia.com/ and is not licensed under GNU Lesser.     //
//                                                                     //
// Modifications by:                                                   //
// Ruben Acuna <flyingfowlsoftware@earthlink.net>                      //
/////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Collections;

namespace MTGProject
{
    // Holds the arguments for the StatusChanged event
    public class StatusChangedEventArgs : EventArgs
    {
        // The argument we're interested in is a message describing the event
        private string EventMsg;

        // Property for retrieving and setting the event message
        public string EventMessage
        {
            get
            {
                return EventMsg;
            }
            set
            {
                EventMsg = value;
            }
        }

        // Constructor for setting the event message
        public StatusChangedEventArgs(string strEventMsg)
        {
            EventMsg = strEventMsg;
        }
    }

    // This delegate is needed to specify the parameters we're passing with our event
    public delegate void StatusChangedEventHandler(object sender, StatusChangedEventArgs e);

    class GameServer
    {
        // This hash table stores users and connections (browsable by user)
        public static Hashtable htUsers = new Hashtable(2); // 2 users at one time limit
        // This hash table stores connections and users (browsable by connection)
        public static Hashtable htConnections = new Hashtable(2); // 2 users at one time limit
        // Will store the IP address passed to it
        private IPAddress ipAddress;
        // The event and its argument will notify the form when a user has connected, disconnected, send message, etc.
        public static event StatusChangedEventHandler StatusChanged;
        private static StatusChangedEventArgs e;
        // The TCP object that listens for connections
        private TcpListener clientConnectionListener;

        // The constructor sets the IP address to the one retrieved by the instantiating object
        public GameServer(IPAddress address)
        {
            ipAddress = address;
        }

        // Add the user to the hash tables
        public static void AddUser(TcpClient user, string name)
        {
            // First add the username and associated connection to both hash tables
            GameServer.htUsers.Add(name, user);
            GameServer.htConnections.Add(user, name);

            // Tell of the new connection to all other users and to the server form
            SendGlobalTell("Admin", htConnections[user] + " has joined us");
        }

        // Remove the user from the hash tables
        public static void RemoveUser(TcpClient user)
        {
            // If the user is there
            if (htConnections[user] != null)
            {
                // First show the information and tell the other users about the disconnection
                SendGlobalTell("Admin", htConnections[user] + " has left us");

                // Remove the user from the hash table
                GameServer.htUsers.Remove(GameServer.htConnections[user]);
                GameServer.htConnections.Remove(user);
            }
        }

        // This is called when we want to raise the StatusChanged event
        public static void OnStatusUpdate(StatusChangedEventArgs e)
        {
            StatusChangedEventHandler statusHandler = StatusChanged;
            if (statusHandler != null)
            {
                // Invoke the delegate
                statusHandler(null, e);
            }
        }

        // Send messages from one user to all the others
        public static void SendGlobalTell(string From, string Message)
        {
            StreamWriter swSenderSender;

            // First of all, show in our application who says what
            e = new StatusChangedEventArgs(From + " says: " + Message);
            OnStatusUpdate(e);

            // Create an array of TCP clients, the size of the number of users we have
            TcpClient[] tcpClients = new TcpClient[GameServer.htUsers.Count];
            // Copy the TcpClient objects into the array
            GameServer.htUsers.Values.CopyTo(tcpClients, 0);
            // Loop through the list of TCP clients
            for (int i = 0; i < tcpClients.Length; i++)
            {
                // Try sending a message to each
                try
                {
                    // If the message is blank or the connection is null, break out
                    if (Message.Trim() == "" || tcpClients[i] == null)
                    {
                        continue;
                    }
                    // Send the message to the current user in the loop
                    swSenderSender = new StreamWriter(tcpClients[i].GetStream());
                    swSenderSender.WriteLine(From + " says: " + Message);
                    swSenderSender.Flush();
                    swSenderSender = null;
                }
                catch // If there was a problem, the user is not there anymore, remove him
                {
                    RemoveUser(tcpClients[i]);
                }
            }
        }

        public void StartListening()
        {
            System.Console.WriteLine("Server is running...\r\n");

            // Create the TCP listener object using the IP of the server and the specified port
            clientConnectionListener = new TcpListener(ipAddress, 1986);

            // Start the TCP listener and listen for connections
            clientConnectionListener.Start();

            ListenForPlayers();
        }

        private void ListenForPlayers()
        {
            PlayerController player1;
            PlayerController player2;

            string players = "";

            while (!(players.Equals("0") || players.Equals("1") || players.Equals("2")))
            {
                System.Console.Clear();
                System.Console.WriteLine("Enter number of human players (0-2):");
                players = System.Console.ReadLine();
            }

            System.Console.WriteLine("Waiting for player 1 to connect...");

            if (players.Equals("0"))
            {
                player1 = new PlayerControllerAIClassic(new Player("Molimo"), "deck-Molimo's Might.xml");
                System.Console.WriteLine(player1.Puppet.Name + " has connected as player 1 and is ready to play.");

                System.Console.WriteLine("Waiting for player 2 to connect...");

                //player2 = new PlayerControllerAI(new Player("The Rat King"), "deck-The Relentless.xml");
                player2 = new PlayerControllerAI(new Player("The Test King"), "deck-cfx-Bant on the March.xml");
                System.Console.WriteLine(player2.Puppet.Name + " has connected as player 2 and is ready to play.");
            }
            else if (players.Equals("1"))
            {
                player1 = new PlayerControllerNetworked(clientConnectionListener.AcceptTcpClient());
                System.Console.WriteLine(player1.Puppet.Name + " has connected as player 1 and is ready to play.");

                System.Console.WriteLine("Waiting for player 2 to connect...");

                player2 = new PlayerControllerAI(new Player("Molimo"), "deck-Molimo's Might.xml");
                System.Console.WriteLine(player2.Puppet.Name + " has connected as player 2 and is ready to play.");
            }
            else //2
            {
                player1 = new PlayerControllerNetworked(clientConnectionListener.AcceptTcpClient());
                System.Console.WriteLine(player1.Puppet.Name + " has connected as player 1 and is ready to play.");

                System.Console.WriteLine("Waiting for player 2 to connect...");

                player2 = new PlayerControllerNetworked(clientConnectionListener.AcceptTcpClient());
                System.Console.WriteLine(player2.Puppet.Name + " has connected as player 2 and is ready to play.");
            }
            Program.startGame(player1, player2);
        }
    }
}
